/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.yammp.fragment;

import org.yammp.R;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.provider.MediaStore;
import android.provider.MediaStore.Video;
import android.view.View;
import android.widget.MediaController;
import android.widget.VideoView;

public class MovieViewControl implements MediaPlayer.OnErrorListener,
		MediaPlayer.OnCompletionListener {

	@SuppressWarnings("unused")
	private static final String TAG = "MovieViewControl";

	private static final int ONE_MINUTE = 60 * 1000;
	private static final int TWO_MINUTES = 2 * ONE_MINUTE;
	private static final int FIVE_MINUTES = 5 * ONE_MINUTE;

	// Copied from MediaPlaybackService in the Music Player app. Should be
	// public, but isn't.
	private static final String SERVICECMD = "com.android.music.musicservicecommand";
	private static final String CMDNAME = "command";
	private static final String CMDPAUSE = "pause";

	private final VideoView mVideoView;
	private final View mProgressView;
	private final Uri mUri;
	private final ContentResolver mContentResolver;

	// State maintained for proper onPause/OnResume behaviour.
	private int mPositionWhenPaused = -1;
	private boolean mWasPlayingWhenPaused = false;
	private MediaController mMediaController;

	Handler mHandler = new Handler();

	Runnable mPlayingChecker = new Runnable() {

		@Override
		public void run() {
			if (mVideoView.isPlaying()) {
				mProgressView.setVisibility(View.GONE);
			} else {
				mHandler.postDelayed(mPlayingChecker, 250);
			}
		}
	};

	public MovieViewControl(View view, Context context, Uri uri) {
		mContentResolver = context.getContentResolver();
		mVideoView = (VideoView) view.findViewById(R.id.surface_view);
		mProgressView = view.findViewById(R.id.progress_indicator);

		mUri = uri;

		/*
		 * For streams that we expect to be slow to start up, show a progress
		 * spinner until playback starts.
		 */
		String scheme = mUri.getScheme();
		if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)) {
			mHandler.postDelayed(mPlayingChecker, 250);
		} else {
			mProgressView.setVisibility(View.GONE);
		}

		mVideoView.setOnErrorListener(this);
		mVideoView.setOnCompletionListener(this);
		mVideoView.setVideoURI(mUri);
		mMediaController = new MediaController(context);
		mVideoView.setMediaController(mMediaController);

		/* make the video view handle keys for seeking and pausing */
		mVideoView.requestFocus();

		Intent i = new Intent(SERVICECMD);
		i.putExtra(CMDNAME, CMDPAUSE);
		context.sendBroadcast(i);

		final Integer bookmark = getBookmark();
		if (bookmark != null) {
			mVideoView.seekTo(bookmark);
			mVideoView.start();
		} else {
			mVideoView.start();
		}
	}

	public void onCompletion() {
	}

	@Override
	public void onCompletion(MediaPlayer mp) {
		onCompletion();
	}

	@Override
	public boolean onError(MediaPlayer player, int arg1, int arg2) {
		mHandler.removeCallbacksAndMessages(null);
		mProgressView.setVisibility(View.GONE);
		return false;
	}

	public void onPause() {
		mHandler.removeCallbacksAndMessages(null);
		setBookmark(mVideoView.getCurrentPosition());

		mPositionWhenPaused = mVideoView.getCurrentPosition();
		mWasPlayingWhenPaused = mVideoView.isPlaying();
		mVideoView.stopPlayback();
	}

	public void onResume() {
		if (mPositionWhenPaused >= 0) {
			mVideoView.setVideoURI(mUri);
			mVideoView.seekTo(mPositionWhenPaused);
			mPositionWhenPaused = -1;
			if (mWasPlayingWhenPaused) {
				mMediaController.show(0);
			}
		}
	}

	private Integer getBookmark() {
		if (!uriSupportsBookmarks(mUri)) return null;

		String[] projection = new String[] { Video.VideoColumns.DURATION,
				Video.VideoColumns.BOOKMARK };

		try {
			Cursor cursor = mContentResolver.query(mUri, projection, null, null, null);
			if (cursor != null) {
				try {
					if (cursor.moveToFirst()) {
						int duration = getCursorInteger(cursor, 0);
						int bookmark = getCursorInteger(cursor, 1);
						if (bookmark < TWO_MINUTES || duration < FIVE_MINUTES
								|| bookmark > duration - ONE_MINUTE) return null;
						return Integer.valueOf(bookmark);
					}
				} finally {
					cursor.close();
				}
			}
		} catch (SQLiteException e) {
			// ignore
		}

		return null;
	}

	private void setBookmark(int bookmark) {
		if (!uriSupportsBookmarks(mUri)) return;

		ContentValues values = new ContentValues();
		values.put(Video.VideoColumns.BOOKMARK, Integer.toString(bookmark));
		try {
			mContentResolver.update(mUri, values, null, null);
		} catch (SecurityException ex) {
			// Ignore, can happen if we try to set the bookmark on a read-only
			// resource such as a video attached to GMail.
		} catch (SQLiteException e) {
			/*
			 * ignore. can happen if the content doesn't support a bookmark
			 * column.
			 */
		} catch (UnsupportedOperationException e) {
			/* ignore. can happen if the external volume is already detached. */
		}
	}

	private static int getCursorInteger(Cursor cursor, int index) {
		try {
			return cursor.getInt(index);
		} catch (SQLiteException e) {
			return 0;
		} catch (NumberFormatException e) {
			return 0;
		}

	}

	private static boolean uriSupportsBookmarks(Uri uri) {
		String scheme = uri.getScheme();
		String authority = uri.getAuthority();
		return "content".equalsIgnoreCase(scheme)
				&& MediaStore.AUTHORITY.equalsIgnoreCase(authority);
	}
}
